package com.csh.auth.domain.service.impl;


import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import com.csh.auth.common.enums.AuthUserStatusEnum;
import com.csh.auth.common.enums.IsDeletedFlagEnum;
import com.csh.auth.domain.constants.AuthConstants;
import com.csh.auth.domain.convert.AuthUserBoConverter;
import com.csh.auth.domain.entity.AuthUserBo;
import com.csh.auth.domain.redis.RedisUtil;
import com.csh.auth.domain.service.AuthUserDomainService;
import com.csh.auth.infra.basic.entity.*;
import com.csh.auth.infra.basic.service.*;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;


@Service
@Slf4j
public class AuthUserDomainServiceImpl implements AuthUserDomainService {

    @Resource
    private AuthUserService authUserService;

    @Resource
    private AuthUserRoleService authUserRoleService;

    @Resource
    private AuthRoleService authRoleService;

    @Resource
    private AuthPermissionService authPermissionService;

    @Resource
    private AuthRolePermissionService authRolePermissionService;

    private String salt = "csh";

    @Resource
    private RedisUtil redisUtil;

    private String authPermissionPrefix = "auth.permission";

    private String authRolePrefix = "auth.role";

    private static final String LOGIN_PREFIX = "loginCode";

    /**
     * 用户注册，（用户扫描，输入验证码登陆后，会根据openid作为用户名创建用户，然后做一些角色与权限的关联）
     * @param authUserBo
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean register(AuthUserBo authUserBo) {
        //根据用户名校验用户是否存在，存在的话直接就返回true了，不存在则注册
        AuthUser existCondition = new AuthUser();
        existCondition.setUserName(authUserBo.getUserName());
        List<AuthUser> existUser = authUserService.queryByCondition(existCondition);
        if (existUser.size() > 0){
            return true;
        }
        //如果用户密码不为空才加密
        AuthUser authUser = AuthUserBoConverter.INSTANCE.convertBoToEntity(authUserBo);
        if (!StringUtils.isBlank(authUser.getPassword())){
            authUser.setPassword(SaSecureUtil.md5BySalt(authUser.getPassword(),salt));
        }
        //用户状态启用、未删除
        authUser.setStatus(AuthUserStatusEnum.OPEN.getCode());
        authUser.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
        //操作用户表，插入数据库
        Integer count = authUserService.insert(authUser);
        /**
         * 建立一个初步的 用户_角色 的关联
         */
        //创建角色查询条件,普通用户角色,未删除
        AuthRole authRole = new AuthRole();
        authRole.setRoleKey(AuthConstants.NORMAL_USER);
        authRole.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
        //查角色表，根据角色唯一标识roleKey查询，返回role对象
        AuthRole roleResult = authRoleService.queryByCondition(authRole);
        //获取roleId和userId
        Long roleId = roleResult.getId();
        //userId之所以能获取是因为，xml文件里插入用户数据的同时，也返回了该用户的userId
        Long userId = authUser.getId();
        //创建 user_role 的插入条件，（用户、角色关联表）
        AuthUserRole authUserRole = new AuthUserRole();
        authUserRole.setUserId(userId);
        authUserRole.setRoleId(roleId);
        authUserRole.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
        //操作用户、角色关联表，新增一条该用户与角色的关联关系
        authUserRoleService.insert(authUserRole);
        //构建缓存key（auth.role.用户名）
        String roleKey = redisUtil.buildKey(authRolePrefix,authUser.getUserName());
        // 将用户的 角色列表 放入缓存中
        List<AuthRole> authRoleList = new LinkedList<>();
        authRoleList.add(authRole);
        redisUtil.set(roleKey,new Gson().toJson(authRoleList));

        /**
         * 根据roleId，查对应的权限对象列表
         */
        //先查role_permission表，构造查询条件
        AuthRolePermission authRolePermission = new AuthRolePermission();
        authRolePermission.setRoleId(roleId);
        //查role_permission表，拿到对应的role_permission对象列表
        List<AuthRolePermission> authRolePermissionList =
                authRolePermissionService.queryByCondition(authRolePermission);
        //获取role_permission对象列表的 权限Id列表
        List<Long> permissionIdList = authRolePermissionList.stream()
                .map(AuthRolePermission::getPermissionId).collect(Collectors.toList());
        //查权限表，根据权限Id列表，查到对应的权限对象列表
        List<AuthPermission> authPermissionList = authPermissionService.queryByPermissionIdList(permissionIdList);

        //构建缓存key,将角色的 权限列表 放入缓存中
        String permissionKey = redisUtil.buildKey(authPermissionPrefix,authUser.getUserName());
        redisUtil.set(permissionKey,new Gson().toJson(authPermissionList));

        return count > 0;
    }

    /**
     * 更新用户信息
     * @param authUserBo
     * @return
     */
    @Override
    public Boolean update(AuthUserBo authUserBo) {
        AuthUser authUser = AuthUserBoConverter.INSTANCE.convertBoToEntity(authUserBo);
        authUser.setStatus(AuthUserStatusEnum.OPEN.getCode());
        authUser.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
        Integer count = authUserService.update(authUser);
        //有任何的更新，都要与缓存进行同步的修改
        return count > 0;
    }

    @Override
    public Boolean delete(AuthUserBo authUserBo) {
        AuthUser authUser = new AuthUser();
        authUser.setId(authUserBo.getId());
        authUser.setIsDeleted(IsDeletedFlagEnum.DELETED.getCode());
        Integer count = authUserService.update(authUser);
        return count > 0;
    }

    @Override
    public Boolean changeStatus(AuthUserBo authUserBo) {
        AuthUser authUser = AuthUserBoConverter.INSTANCE.convertBoToEntity(authUserBo);
        Integer count = authUserService.update(authUser);
        //有任何的更新，都要与缓存进行同步的修改
        return count > 0;
    }

    //用户输入验证码登陆时调用
    @Override
    public SaTokenInfo doLogin(String validCode) {
        String loginKey = redisUtil.buildKey(LOGIN_PREFIX, validCode);
        String openid = redisUtil.get(loginKey);
        if (StringUtils.isBlank(openid)){
            return null;
        }
        AuthUserBo authUserBo = new AuthUserBo();
        authUserBo.setUserName(openid);
        this.register(authUserBo);
        StpUtil.login(openid);
        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
        return tokenInfo;
    }

    @Override
    public AuthUserBo getUserInfo(AuthUserBo authUserBo) {
        AuthUser authUser = new AuthUser();
        authUser.setUserName(authUserBo.getUserName());
        List<AuthUser> userList = authUserService.queryByCondition(authUser);
        if (CollectionUtils.isEmpty(userList)){
            return new AuthUserBo();
        }
        AuthUser userInfo = userList.get(0);
        return AuthUserBoConverter.INSTANCE.convertEntityToBo(userInfo);
    }


}
